
#include "ipfe_image_pyramid.h"

// [*] CImagePyramidPosition

void CImagePyramidPosition::InitScale(double xOld, double yOld, double xNew, double yNew)
{
	this->xOld = xOld; this->yOld = yOld;
	this->xNew = xNew; this->yNew = yNew;
	this->xScale = xNew / xOld;
	this->yScale = yNew / yOld;
}

void CImagePyramidPosition::InitNew(double xOld, double yOld, double xScale, double yScale)
{
	this->xOld = xOld; this->yOld = yOld;
	this->xScale = xScale; this->yScale = yScale;
	this->xNew = xOld * xScale;
	this->yNew = yOld * yScale;
}

void CImagePyramidPosition::InitOld(double xNew, double yNew, double xScale, double yScale)
{
	this->xNew = xNew; this->yNew = yNew;
	this->xScale = xScale; this->yScale = yScale;
	this->xOld = xNew / xScale;
	this->yOld = yNew / yScale;
}

bool CImagePyramidPosition::operator < (const CImagePyramidPosition& AnotherInstance) const
{
	double t;
	t = this->yScale - AnotherInstance.yScale;
	if (t<-EPS || t>EPS) return t<-EPS;
	t = this->xScale - AnotherInstance.xScale;
	if (t<-EPS || t>EPS) return t<-EPS;
	t = this->yOld - AnotherInstance.yOld;
	if (t<-EPS || t>EPS) return t<-EPS;
	t = this->xOld - AnotherInstance.xOld;
	if (t<-EPS || t>EPS) return t<-EPS;
	return false;
}

bool CImagePyramidPosition::InputFromStream(istream& inStream)
{
	if (!(inStream>>xOld>>yOld>>xScale>>yScale)) return false;
	InitNew(xOld, yOld, xScale, yScale);
	return true;
}

void CImagePyramidPosition::OutputToStream(ostream& outStream) const
{
	outStream.precision(6);
	outStream<<"\t"<<fixed<<xOld;
	outStream<<"\t"<<fixed<<yOld;
	outStream<<"\t"<<fixed<<xScale;
	outStream<<"\t"<<fixed<<yScale;
}

//size_t CImagePyramidPosition::GetMemoryUsage() const
//{
//	return sizeof(double) * 6;
//}

// [*] CImagePyramidPositionFeatureExtractor

void CImagePyramidPositionFeatureExtractor::myRelease()
{
	int i;
	CImagePositionFeatureExtractor** ptr;

	ptr = IPFE_Pool_IPFE_List.pElement;
	for ( i = 0; i < IPFE_Pool_Size; ++i )
		CDataStructureTools::MyRelease_Single(*(ptr++));
	IPFE_Pool_Size = 0;
	IPFE_Pool_IPFE_List.Clear();
	ClearCalculationHistory();
}

CImagePyramidPositionFeatureExtractor::CImagePyramidPositionFeatureExtractor()
{
	IPFE_Pool_Size = 0;
	IPFE_Pool_IPFE_List.Clear();
	InitIPFE_and_Cache(NULL, 0);
}

CImagePyramidPositionFeatureExtractor::CImagePyramidPositionFeatureExtractor(CImagePositionFeatureExtractor* pInitializedIPFE, int CacheSize)
{
	IPFE_Pool_Size = 0;
	IPFE_Pool_IPFE_List.Clear();
	InitIPFE_and_Cache(pInitializedIPFE, CacheSize);
}

CImagePyramidPositionFeatureExtractor::~CImagePyramidPositionFeatureExtractor()
{
	myRelease();
}

CImagePyramidPositionFeatureExtractor& CImagePyramidPositionFeatureExtractor::operator = (const CImagePyramidPositionFeatureExtractor& AnotherInstance)
{
	if (this != &AnotherInstance)
	{
		CImagePositionFeatureExtractor* tPtr = NULL;
		if (AnotherInstance.IPFE_Pool_Size>0 && AnotherInstance.IPFE_Pool_IPFE_List.pElement[0]!=NULL)
			tPtr = AnotherInstance.IPFE_Pool_IPFE_List.pElement[0]->myClone();
		InitIPFE_and_Cache(tPtr, AnotherInstance.IPFE_Pool_Size);
	}
	return (*this);
}

void CImagePyramidPositionFeatureExtractor::InitIPFE_and_Cache(CImagePositionFeatureExtractor* pInitializedIPFE, int CacheSize)
{
	int i;

	myRelease();
	if (pInitializedIPFE==NULL || CacheSize<0) CacheSize = 0;

	Dim = (pInitializedIPFE==NULL ? 0 : pInitializedIPFE->GetFeatureDimension());
	IPFE_Pool_Size = CacheSize;
	IPFE_Pool_IPFE_List.Resize(IPFE_Pool_Size);
	for ( i = 0; i < IPFE_Pool_Size; ++i )
		IPFE_Pool_IPFE_List.pElement[i] = (i==0 ? pInitializedIPFE : pInitializedIPFE->myClone());

	ImageHandle = 0;

	ClearCalculationHistory();
}

void CImagePyramidPositionFeatureExtractor::ClearCalculationHistory()
{
	Query.InitNew(1.0, 1.0, 0.0, 0.0);
	LayersUsedCount = 0;
	HistoryCount = 0;
	IPFE_Pool_History_List.Clear(); IPFE_Pool_History_List.Resize(IPFE_Pool_Size, -1);
	IPFE_Pool_xScale_List.Clear(); IPFE_Pool_xScale_List.Resize(IPFE_Pool_Size, 0.0);
	IPFE_Pool_yScale_List.Clear(); IPFE_Pool_yScale_List.Resize(IPFE_Pool_Size, 0.0);

	QueryAnswerer = NULL;
	QueryX = QueryY = -1;
}

int CImagePyramidPositionFeatureExtractor::SetImage(const CImg<int>& imgSrcRGB, string strFN_ImageCacheHashName)
{
	ImageHandle = CMyRandom::NextInt(2147483647)+1;
	this->strFN_ImageCacheHashName = strFN_ImageCacheHashName;

	ZoomImage.myInit(imgSrcRGB);
	//tfmImage.myInit(imgSrcRGB);
	//srkImage.myInit(imgSrcRGB);
	////trsImage.myInit(imgSrcRGB);
	////SourceScalableImage.myInit(imgSrcRGB);
	
	ClearCalculationHistory();

	return ImageHandle;
}

int CImagePyramidPositionFeatureExtractor::GetFeatureDimension()
{
	return Dim;
}

int CImagePyramidPositionFeatureExtractor::RecomputeFeatureDimension()
{
	Dim = 0;
	int i;
	for ( i = 0; i < IPFE_Pool_Size; ++i )
		Dim = IPFE_Pool_IPFE_List.pElement[i]->RecomputeFeatureDimension();
	return Dim;
}

double CImagePyramidPositionFeatureExtractor::GetFeature(int FeatureIndex)
{
	if (QueryAnswerer==NULL || FeatureIndex<0 || FeatureIndex>=Dim) return 0.0;
	return QueryAnswerer->GetFeature(QueryX, QueryY, FeatureIndex);
}

void CImagePyramidPositionFeatureExtractor::SetQuery(const CImagePyramidPosition& ImagePyramidPosition)
{
	if (abs(ImagePyramidPosition.xScale)<EPS || abs(ImagePyramidPosition.yScale)<EPS)
		return;

	double ee_scale = 1.001;
	double ee_position = 1.01;
	double rqx, rqy;

	int i, jj;
	double tt, ttx, tty;

	ttx = Query.xScale / ImagePyramidPosition.xScale;
	ttx = (ttx<EPS ? 2.0 : (ttx<1 ? 1/ttx : ttx));
	tty = Query.yScale / ImagePyramidPosition.yScale;
	tty = (tty<EPS ? 2.0 : (tty<1 ? 1/tty : tty));
	tt = ttx * tty;
	if (tt>ee_scale || abs(Query.xScale)<EPS || abs(Query.yScale)<EPS)
	{
		double* p_xScale = IPFE_Pool_xScale_List.pElement;
		double* p_yScale = IPFE_Pool_yScale_List.pElement;
		QueryAnswerer = NULL;
		for ( jj = 0, i = 0; i < IPFE_Pool_Size; ++i )
		{
			if (IPFE_Pool_History_List.pElement[i] < IPFE_Pool_History_List.pElement[jj])
				jj = i;
			ttx = *p_xScale / ImagePyramidPosition.xScale;
			ttx = (ttx<EPS ? 2.0 : (ttx<1 ? 1/ttx : ttx));
			tty = *p_yScale / ImagePyramidPosition.yScale;
			tty = (tty<EPS ? 2.0 : (tty<1 ? 1/tty : tty));
			tt = ttx * tty;
			if (!(tt>ee_scale || abs(*p_xScale)<EPS || abs(*p_yScale)<EPS))
			{
				IPFE_Pool_History_List.pElement[i] = HistoryCount++;
				QueryAnswerer = IPFE_Pool_IPFE_List.pElement[i];
				break;
			}
			++p_xScale; ++p_yScale;
		}
		if (QueryAnswerer == NULL)
		{
			//i = CMyRandom::NextInt(IPFE_Pool_Size);
			i = jj;
			++LayersUsedCount;
			IPFE_Pool_History_List.pElement[i] = HistoryCount++;

			p_xScale = IPFE_Pool_xScale_List.pElement + i;
			p_yScale = IPFE_Pool_yScale_List.pElement + i;
			*p_xScale = ImagePyramidPosition.xScale;
			*p_yScale = ImagePyramidPosition.yScale;

			ZoomImage.BuildImage(QueryImageLayer, *p_xScale, *p_yScale, CDenseImage::EXPANDING_MODE_EDGE);
			//QueryImageLayer.display();

			char strTemp[10001];
			strTemp[0] = 0;
			if (strFN_ImageCacheHashName != "")
			{
				int xx = CMyMath::TranslateNumber_Round(log(abs(*p_xScale))/log(1/1.025), (int)0);
				int yy = CMyMath::TranslateNumber_Round(log(abs(*p_yScale))/log(1/1.025), (int)0);
				sprintf_s(strTemp, "%s.%c%d.%c%d",
					strFN_ImageCacheHashName.c_str(),
					(*p_xScale>0 ? 'P' : 'N'), xx,
					(*p_yScale>0 ? 'P' : 'N'), yy);
			}
			QueryAnswerer = IPFE_Pool_IPFE_List.pElement[i];
			QueryAnswerer->SetImage(QueryImageLayer, strTemp);
		}
		Query.InitNew(ImagePyramidPosition.xOld, ImagePyramidPosition.yOld, *p_xScale, *p_yScale);
		rqx = Query.xNew; if (rqx < 0) rqx += QueryImageLayer.width;
		rqy = Query.yNew; if (rqy < 0) rqy += QueryImageLayer.height;
		QueryX = (int)rqx;
		QueryY = (int)rqy;
	}
	else
	{
		ttx = 1.0 + abs(Query.xNew - abs(ImagePyramidPosition.xNew));
		tty = 1.0 + abs(Query.yNew - abs(ImagePyramidPosition.yNew));
		tt = ttx * tty;
		if (tt > ee_position)
		{
			Query.InitNew(ImagePyramidPosition.xOld, ImagePyramidPosition.yOld, Query.xScale, Query.yScale);
			rqx = Query.xNew; if (rqx < 0) rqx += QueryImageLayer.width;
			rqy = Query.yNew; if (rqy < 0) rqy += QueryImageLayer.height;
			QueryX = (int)rqx;
			QueryY = (int)rqy;
		}
	}
}

double CImagePyramidPositionFeatureExtractor::GetFeature(const CImagePyramidPosition& ImagePyramidPosition, int FeatureIndex)
{
	SetQuery(ImagePyramidPosition);
	return GetFeature(FeatureIndex);
}

size_t CImagePyramidPositionFeatureExtractor::GetMemoryUsage() const
{
	size_t ret = sizeof(int) * 4
		+ IPFE_Pool_IPFE_List.GetMemoryUsage()
		+ IPFE_Pool_xScale_List.GetMemoryUsage()
		+ IPFE_Pool_yScale_List.GetMemoryUsage()
		+ sizeof(Query)
		+ sizeof(CImagePositionFeatureExtractor*)
		+ CExternalTools::GetMemoryUsage(QueryImageLayer)
		+ ZoomImage.GetMemoryUsage();
		////+ trsImage.GetMemoryUsage();
		//+ tfmImage.GetMemoryUsage()
		//+ srkImage.GetMemoryUsage();
	int i;
	for ( i = 0; i < IPFE_Pool_Size; ++i )
		ret += IPFE_Pool_IPFE_List.pElement[i]->GetMemoryUsage();
	return ret;
}

